# coding=utf-8
# 2019/12/12: 首个记录
# 2020/4/1: 导入bi_common
# 2020/7/28: 完善枚举类型定义
# 2020/8/3: 增加枚举类型==/!=重载
# 2020/8/5: 枚举值类改为继承EnumValue
# 2020/8/26: 支持v3 (输出仍为v2)
# 2020/11/3: 新增气压、相对湿度、能见度、风向、风速、PM10、道路状况、车道交通状况、灾害情况等字段。道路交通状况更名。输出v3

import bi_common as bi


# 天气类型
class WeatherTypeValue(bi.EnumValue):
    def __str__(self):
        if self.val == 1:
            return 'SUNNY'
        elif self.val == 2:
            return 'CLOUDY'
        elif self.val == 3:
            return 'RAINY'
        elif self.val == 4:
            return 'SNOWY'
        elif self.val == 5:
            return 'FOGGY'
        elif self.val == 6:
            return 'SAND'
        else:
            return 'UNKNOWN'


class WeatherType:
    UNKNOWN = WeatherTypeValue(0)  # 未知
    SUNNY = WeatherTypeValue(1)  # 晴
    CLOUDY = WeatherTypeValue(2)  # 阴
    RAINY = WeatherTypeValue(3)  # 雨
    SNOWY = WeatherTypeValue(4)  # 雪
    FOGGY = WeatherTypeValue(5)  # 雾
    SAND = WeatherTypeValue(6)  # 沙尘

    def __init__(self):
        pass


# 道路类型
class RoadTypeValue(bi.EnumValue):
    def __str__(self):
        if self.val == 1:
            return 'HIGHWAY'
        elif self.val == 2:
            return 'CITY_EXPRESS'
        elif self.val == 3:
            return 'CITY_MAIN'
        elif self.val == 4:
            return 'GENERAL_ROAD'
        elif self.val == 5:
            return 'COUNTRY_ROAD'
        else:
            return 'UNKNOWN'


class RoadType:
    UNKNOWN = RoadTypeValue(0)  # 未知
    HIGHWAY = RoadTypeValue(1)  # 高速路，无红绿灯，80~120KPH
    CITY_EXPRESS = RoadTypeValue(2)  # 城市快速路，无红绿灯，60~80KPH
    CITY_MAIN = RoadTypeValue(3)  # 城市主干道，有红绿灯，推荐60KPH
    GENERAL_ROAD = RoadTypeValue(4)  # 一般道路，有红绿灯，推荐40KPH
    COUNTRY_ROAD = RoadTypeValue(5)  # 乡村道路，无红绿灯，~40KPH

    def __init__(self):
        pass


# 场所类型
class SpotTypeValue(bi.EnumValue):
    def __str__(self):
        if self.val == 1:
            return 'TOLL_GATE'
        elif self.val == 2:
            return 'SERVICE_AREA'
        elif self.val == 3:
            return 'TUNNEL'
        elif self.val == 4:
            return 'ACCIDENT'
        elif self.val == 5:
            return 'CROSSING'
        elif self.val == 7:
            return 'ROUNDABOUT'
        elif self.val == 8:
            return 'HIGHWAY_ENTRANCE'
        elif self.val == 9:
            return 'HIGHWAY_EXIT'
        elif self.val == 10:
            return 'BRIDGE'
        elif self.val == 11:
            return 'EXPRESS_ENTRANCE'
        elif self.val == 12:
            return 'EXPRESS_EXIT'
        else:
            return 'UNKNOWN'


class SpotType:
    UNKNOWN = SpotTypeValue(0)  # 收费站
    TOLL_GATE = SpotTypeValue(1)  # 不响
    SERVICE_AREA = SpotTypeValue(2)  # 服务区
    TUNNEL = SpotTypeValue(3)  # 隧道
    ACCIDENT = SpotTypeValue(4)  # 事故
    CROSSING = SpotTypeValue(5)  # 路口
    ROUNDABOUT = SpotTypeValue(7)  # 环岛
    HIGHWAY_ENTRANCE = SpotTypeValue(8)  # 高速入口
    HIGHWAY_EXIT = SpotTypeValue(9)  # 高速出口
    BRIDGE = SpotTypeValue(10)  # 桥
    EXPRESS_ENTRANCE = SpotTypeValue(11)  # 快速路入口
    EXPRESS_EXIT = SpotTypeValue(12)  # 快速路出口

    def __init__(self):
        pass


# 场所信息
class Spot:
    def __init__(self):
        self.spot_type = SpotType.UNKNOWN  # 场所类型
        self.distance = 0.0  # 到场所的距离 m


# 交通状况
class TrafficStatusValue(bi.EnumValue):
    def __str__(self):
        if self.val == 1:
            return 'OPEN'
        elif self.val == 2:
            return 'SLOW'
        elif self.val == 3:
            return 'CROWDED'
        elif self.val == 4:
            return 'REVERSE'
        else:
            return 'UNKNOWN'


class TrafficStatus:
    UNKNOWN = TrafficStatusValue(0)  # 未知
    OPEN = TrafficStatusValue(1)  # 畅通
    SLOW = TrafficStatusValue(2)  # 缓慢
    CROWDED = TrafficStatusValue(3)  # 拥挤
    REVERSE = TrafficStatusValue(4)  # 逆向（仅限相邻车道交通状况）

    def __init__(self):
        pass


# 风向
class WindDirectionValue(bi.EnumValue):
    def __str__(self):
        if self.val == 1:
            return 'EAST'
        elif self.val == 2:
            return 'SOUTH'
        elif self.val == 3:
            return 'WEST'
        elif self.val == 4:
            return 'NORTH'
        elif self.val == 5:
            return 'SOUTH_EAST'
        elif self.val == 6:
            return 'NORTH_EAST'
        elif self.val == 7:
            return 'SOUTH_WEST'
        elif self.val == 8:
            return 'NORTH_WEST'
        else:
            return 'UNKNOWN'


class WindDirection:
    UNKNOWN = WindDirectionValue(0)  # 未知
    EAST = WindDirectionValue(1)  # 东风
    SOUTH = WindDirectionValue(2)  # 南风
    WEST = WindDirectionValue(3)  # 西风
    NORTH = WindDirectionValue(4)  # 北风
    SOUTH_EAST = WindDirectionValue(5)  # 东南风
    NORTH_EAST = WindDirectionValue(6)  # 东北风
    SOUTH_WEST = WindDirectionValue(7)  # 西南风
    NORTH_WEST = WindDirectionValue(8)  # 西北风

    def __init__(self):
        pass


# 路面状况
class RoadSurfaceStatusValue(bi.EnumValue):
    def __str__(self):
        if self.val == 1:
            return 'DRY'
        elif self.val == 2:
            return 'WET'
        elif self.val == 3:
            return 'SNOW_COVERED'
        elif self.val == 4:
            return 'FROZEN'
        else:
            return 'UNKNOWN'


class RoadSurfaceStatus:
    UNKNOWN = RoadSurfaceStatusValue(0)  # 未知
    DRY = RoadSurfaceStatusValue(1)  # 干燥
    WET = RoadSurfaceStatusValue(2)  # 湿滑
    SNOW_COVERED = RoadSurfaceStatusValue(3)  # 积雪
    FROZEN = RoadSurfaceStatusValue(4)  # 结冰

    def __init__(self):
        pass


# 灾害状况
class DisasterStatusValue(bi.EnumValue):
    def __str__(self):
        if self.val == 1:
            return 'NO'
        elif self.val == 2:
            return 'YES'
        else:
            return 'UNKNOWN'


class DisasterStatus:
    UNKNOWN = DisasterStatusValue(0)  # 未知
    NO = DisasterStatusValue(1)  # 无灾害
    YES = DisasterStatusValue(2)  # 有灾害

    def __init__(self):
        pass


# 环境信息样本
class EnvSample:
    def __init__(self):
        self.time = 0.0  # 时间戳 s
        self.weather_type = WeatherType.UNKNOWN  # 天气类型
        self.temperature_valid = False
        self.temperature = 0.0  # 温度 °C
        self.humidity_valid = False
        self.humidity = 0.0  # 相对湿度 %
        self.pressure_valid = False
        self.pressure = 0.0  # 气压 mb
        self.pm25_valid = False
        self.pm25 = 0.0  # PM 2.5 ug/m3
        self.pm10_valid = False
        self.pm10 = 0.0  # ug/m3
        self.visibility_valid = False
        self.visibility = 0.0  # 能见度 KM
        self.illumination_valid = False
        self.illumination = 0.0  # 光照强度 lux
        self.wind_speed_valid = False
        self.wind_speed = 0.0  # 风速 m/s
        self.wind_direction = WindDirection.UNKNOWN  # 风向
        self.typhoon_status = DisasterStatus.UNKNOWN  # 台风状况
        self.tornado_status = DisasterStatus.UNKNOWN  # 龙卷风状况
        self.lightning_status = DisasterStatus.UNKNOWN  # 雷电状况
        self.hail_status = DisasterStatus.UNKNOWN  # 冰雹状况
        self.road_name = None  # 道路名称
        self.speed_limit_valid = False
        self.speed_limit = 0  # 道路限速 KPH
        self.road_type = RoadType.UNKNOWN  # 道路类型
        self.road_surface_status = RoadSurfaceStatus.UNKNOWN  # 路面状况
        self.road_traffic = TrafficStatus.UNKNOWN  # 道路交通状况
        self.current_lane_traffic = TrafficStatus.UNKNOWN  # 本车道交通状况
        self.left_lane_traffic = TrafficStatus.UNKNOWN  # 左车道交通状况
        self.right_lane_traffic = TrafficStatus.UNKNOWN  # 右车道交通状况
        self.spots = []  # 场所列表

    # 转通用样本，用于样本输出
    def to_general_sample(self):
        output = bi.agency.create_general_sample()
        output.protocol = "env-sample-v3"
        output.time = self.time
        road_name_bin = bytearray(self.road_name, encoding='utf-8') if self.road_name is not None else []
        output.significant = 64 + len(self.spots) * 2 + len(road_name_bin)
        output.values = []
        output.values.append(len(road_name_bin))
        output.values.append(len(self.spots))
        output.values.append(self.weather_type.val)
        output.values.append(self.temperature if self.temperature_valid else None)
        output.values.append(self.pm25 if self.pm25_valid else None)
        output.values.append(self.speed_limit if self.speed_limit_valid else None)
        output.values.append(self.road_type.val)
        output.values.append(self.illumination if self.illumination_valid else None)
        output.values.append(self.wind_speed if self.wind_speed_valid else None)
        output.values.append(self.road_traffic.val)
        output.values.append(None)  # reserved
        output.values.append(self.pressure if self.pressure_valid else None)
        output.values.append(self.humidity if self.humidity_valid else None)
        output.values.append(self.visibility if self.visibility_valid else None)
        output.values.append(self.wind_direction.val)
        output.values.append(self.pm10 if self.pm10_valid else None)
        output.values.append(self.road_surface_status.val)
        output.values.append(self.current_lane_traffic.val)
        output.values.append(self.left_lane_traffic.val)
        output.values.append(self.right_lane_traffic.val)
        output.values.append(self.typhoon_status.val)
        output.values.append(self.tornado_status.val)
        output.values.append(self.lightning_status.val)
        output.values.append(self.hail_status.val)
        for i in range(0, 40):
            output.values.append(None)  # reserved
        for spot in self.spots:
            if isinstance(spot, Spot):
                output.values.append(spot.spot_type.val)
                output.values.append(spot.distance)
            else:
                output.values.append(SpotType.UNKNOWN)
                output.values.append(10000.0)
        for b in road_name_bin:
            output.values.append(int(b))
        return output


def _conv_env_sample_v3(gs):
    values_count = len(gs.values)
    if values_count < 2:
        return None
    road_name_bin_size = int(gs.values[0]) if gs.values[0] is not None else 0
    spot_count = int(gs.values[1]) if gs.values[1] is not None else 0
    if values_count != 64 + 2 * spot_count + road_name_bin_size:
        return None
    output = EnvSample()
    output.time = gs.time
    output.weather_type = WeatherTypeValue(int(gs.values[2])) if gs.values[2] is not None else WeatherType.UNKNOWN
    if gs.values[3] is not None:
        output.temperature_valid = True
        output.temperature = float(gs.values[3])
    if gs.values[4] is not None:
        output.pm25_valid = True
        output.pm25 = float(gs.values[4])
    if gs.values[5] is not None:
        output.speed_limit_valid = True
        output.speed_limit = int(gs.values[5])
    output.road_type = RoadTypeValue(int(gs.values[6])) if gs.values[6] is not None else RoadType.UNKNOWN
    if gs.values[7] is not None:
        output.illumination_valid = True
        output.illumination = float(gs.values[7])
    if gs.values[8] is not None:
        output.wind_speed_valid = True
        output.wind_speed = float(gs.values[8])
    output.road_traffic = TrafficStatusValue(int(gs.values[9])) if gs.values[9] is not None else TrafficStatus.UNKNOWN
    if gs.values[11] is not None:
        output.pressure_valid = True
        output.pressure = float(gs.values[11])
    if gs.values[12] is not None:
        output.humidity_valid = True
        output.humidity = float(gs.values[12])
    if gs.values[13] is not None:
        output.visibility_valid = True
        output.visibility = float(gs.values[13])
    if gs.values[14] is not None:
        output.wind_direction = WindDirectionValue(int(gs.values[14]))
    if gs.values[15] is not None:
        output.pm10_valid = True
        output.pm10 = float(gs.values[15])
    if gs.values[16] is not None:
        output.road_surface_status = RoadSurfaceStatusValue(int(gs.values[16]))
    if gs.values[17] is not None:
        output.current_lane_traffic = TrafficStatusValue(int(gs.values[17]))
    if gs.values[18] is not None:
        output.left_lane_traffic = TrafficStatusValue(int(gs.values[18]))
    if gs.values[19] is not None:
        output.right_lane_traffic = TrafficStatusValue(int(gs.values[19]))
    if gs.values[20] is not None:
        output.typhoon_status = DisasterStatusValue(int(gs.values[20]))
    if gs.values[21] is not None:
        output.tornado_status = DisasterStatusValue(int(gs.values[21]))
    if gs.values[22] is not None:
        output.lightning_status = DisasterStatusValue(int(gs.values[22]))
    if gs.values[23] is not None:
        output.hail_status = DisasterStatusValue(int(gs.values[23]))
    for i in range(0, spot_count):
        if gs.values[64 + 2 * i] is not None and gs.values[65 + 2 * i] is not None:
            spot = Spot()
            spot.spot_type = SpotTypeValue(int(gs.values[64 + 2 * i]))
            spot.distance = float(gs.values[65 + 2 * i])
            output.spots.append(spot)
    if gs.values[10] is not None:
        output.road_name = gs.values[10]
    elif road_name_bin_size > 0:
        road_name_bin_ints = []
        for i in range(0, road_name_bin_size):
            b = gs.values[64 + 2 * spot_count + i]
            if b is None:
                road_name_bin_ints = []
                break
            else:
                road_name_bin_ints.append(int(b))
        if len(road_name_bin_ints) > 0:
            output.road_name = bytes(road_name_bin_ints).decode('utf-8')
    return output


def _conv_env_sample_v2(gs):
    values_count = len(gs.values)
    if values_count < 2:
        return None
    road_name_bin_size = int(gs.values[0]) if gs.values[0] is not None else 0
    spot_count = int(gs.values[1]) if gs.values[1] is not None else 0
    if values_count != 10 + 2 * spot_count + road_name_bin_size:
        return None
    output = EnvSample()
    output.time = gs.time
    output.weather_type = WeatherTypeValue(int(gs.values[2])) if gs.values[2] is not None else WeatherType.UNKNOWN
    if gs.values[3] is not None:
        output.temperature_valid = True
        output.temperature = float(gs.values[3])
    if gs.values[4] is not None:
        output.pm25_valid = True
        output.pm25 = float(gs.values[4])
    if gs.values[5] is not None:
        output.speed_limit_valid = True
        output.speed_limit = int(gs.values[5])
    output.road_type = RoadTypeValue(int(gs.values[6])) if gs.values[6] is not None else RoadType.UNKNOWN
    if gs.values[7] is not None:
        output.illumination_valid = True
        output.illumination = float(gs.values[7])
    if gs.values[8] is not None:
        output.wind_speed_valid = True
        output.wind_speed = float(gs.values[8])
    output.road_traffic = TrafficStatusValue(int(gs.values[9])) if gs.values[9] is not None else TrafficStatus.UNKNOWN
    for i in range(0, spot_count):
        if gs.values[10 + 2 * i] is not None and gs.values[11 + 2 * i] is not None:
            spot = Spot()
            spot.spot_type = SpotTypeValue(int(gs.values[10 + 2 * i]))
            spot.distance = float(gs.values[11 + 2 * i])
            output.spots.append(spot)
    road_name_bin_ints = []
    for i in range(0, road_name_bin_size):
        b = gs.values[10 + 2 * spot_count + i]
        if b is None:
            road_name_bin_ints = []
            break
        else:
            road_name_bin_ints.append(int(b))
    if len(road_name_bin_ints) > 0:
        output.road_name = bytes(road_name_bin_ints).decode('utf-8')
    return output


# 获取EnvSample，用于样本输入
def get_env_sample():
    s1 = None
    s2 = None
    w1 = 0.0
    w2 = 0.0
    protocol_id_v2 = 'env-sample-v2'
    protocol_id_v3 = 'env-sample-v3'
    if protocol_id_v3 in bi.input_samples:
        pair = bi.input_samples[protocol_id_v3]
        s1 = _conv_env_sample_v3(pair.sample1)
        w1 = pair.weight1
        s2 = _conv_env_sample_v3(pair.sample2)
        w2 = pair.weight2
    elif protocol_id_v2 in bi.input_samples:
        pair = bi.input_samples[protocol_id_v2]
        s1 = _conv_env_sample_v2(pair.sample1)
        w1 = pair.weight1
        s2 = _conv_env_sample_v2(pair.sample2)
        w2 = pair.weight2
    if s1 is not None and s2 is not None:
        return s1 if w1 > w2 else s2
    return None
